/* -*- mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

#include "mandelbaum.h"
#include <iostream>
#include "math.h"


typedef mpz_class integer;



void CRT(integer &modulo1, integer &residue1, integer modulo2, integer residue2){
	integer inverse;
	integer residue_result;
	residue_result = 0;
	integer modulo_result;
	modulo_result = modulo1 * modulo2;

	mpz_invert(inverse.get_mpz_t(),modulo1.get_mpz_t(),modulo2.get_mpz_t());
	residue_result = residue_result + (residue2)*(modulo1)*(inverse);
	mpz_mod(residue_result.get_mpz_t() , residue_result.get_mpz_t(),modulo_result.get_mpz_t());
	

	mpz_invert(inverse.get_mpz_t(),modulo2.get_mpz_t(),modulo1.get_mpz_t());
	residue_result = residue_result + (residue1)*(modulo2)*(inverse);
	mpz_mod(residue_result.get_mpz_t() , residue_result.get_mpz_t(),modulo_result.get_mpz_t());

	modulo1 = modulo_result;
	residue1 = residue_result;
}




void Lifter::AddOneValue(const long modulo, const long residue){
		
	

	integer Residue,Modulo;
	Residue = residue;
	Modulo = modulo;


	vector<pair<integer,integer> >::iterator it = _RadixTable.begin();
	
	while (it->first != 0 && it != _RadixTable.end())
	{
		CRT(Modulo, Residue, it->first, it->second);
		it->first=0;
		it++;
	}
	it->first = Modulo;
	it->second = Residue;
	
	it++;
	if (it == _RadixTable.end())

	{
		_RadixTable.resize(_RadixTable.size()+1);
	}

	
	if (_MaxModulus < modulo) 
	{
		_MaxModulus = modulo;
	}


}




void Lifter::GetCurrentResult (integer &residue, integer &modulus, integer &modulusMax){
	
	
	modulusMax = _MaxModulus;
	
	

	vector<pair<integer,integer> >::iterator it = _RadixTable.begin();
	it = _RadixTable.begin();

	while (it != _RadixTable.end())
	{
		if (it->first != 0)
		{
			modulus = it->first;
			residue = it->second;
			it++;
			break;
		}
	it++;
	}
	while (it != _RadixTable.end())
	{
		if (it->first != 0)
		{
			CRT(modulus,residue,it->first,it->second);
		}
	it++;
	}
	
}
	

void Decoder::One_Step_Euclide_Etendu(){

	integer ai, ri_plus_1;
	mpz_fdiv_qr(ai.get_mpz_t(), ri_plus_1.get_mpz_t(), _ri_1.get_mpz_t(), _ri.get_mpz_t());
	_pi = (ai*_pi_1) + _pi_2;
	_qi = (ai*_qi_1) + _qi_2;

	
	_ri_1 = _ri;
	_ri = ri_plus_1;
	_pi_2 = _pi_1;
	_pi_1 = _pi;
	_qi_2 = _qi_1;
	_qi_1 = _qi;

}

void Decoder::BoundDecode_And_SendResults(decoderCommunicator comm){

	
    comm.writeToFrontend(_residue, 0);
    integer temp, Erreur, IsInt;
	


    int i,j,constante;
	i = 0;
    if (_ri != 0)
    {
        One_Step_Euclide_Etendu();
	}
    while ((_qi < _bound) && (_ri != 0))
    {

		temp = _modulus * _pi;
        mpz_fdiv_qr(Erreur.get_mpz_t(), IsInt.get_mpz_t(), temp.get_mpz_t(), _qi.get_mpz_t());
        if (IsInt == 0)
		{
            if (_residue - Erreur >= 0)
            {	
                j=1;
                temp = _MaxModulus;
                while (temp < _qi)
				{
                    temp = temp * _MaxModulus;
					j++;
                }
                comm.writeToFrontend(_residue - Erreur, j); 
				

            }
        }	
        One_Step_Euclide_Etendu();

	i++;
    }

    comm.writeEndOfResultToFrontend();

}









